﻿using System.Reflection;
using System.Web.Routing;

namespace System.Web.Mvc
{
    public class UrlToHelper<TController> where TController : Controller
    {
        public UrlToHelper(UrlHelper urlHelper)
        {
            this.UrlHelper = urlHelper;
        }

        public UrlHelper UrlHelper { get; private set; }

        public string Action(MethodInfo action, object[] args, object routeValues)
        {
            VirtualPathData path = this.UrlHelper.RouteCollection.GetVirtualPath(this.UrlHelper.RequestContext, action, args, routeValues);
            if (path == null)
            {
                string message = String.Format("Url generation failed for {0}", action);
                throw new ArgumentException(message);
            }
            return path.VirtualPath;
        }

        #region no arguments

        public string Action(Func<TController, Func<object>> action)
        {
            return Action(action, null);
        }

        public string Action(Func<TController, Func<object>> action, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[0], routeValues);
        }

        public string Action(Func<TController, Action> action)
        {
            return Action(action, null);
        }

        public string Action(Func<TController, Action> action, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[0], routeValues);
        }

        #endregion

        #region 1 argument

        public string Action<T>(Func<TController, Func<T, object>> action, T arg)
        {
            return Action(action, arg, null);
        }

        public string Action<T>(Func<TController, Func<T, object>> action, T arg, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[] { arg }, routeValues);
        }

        public string Action<T>(Func<TController, Action<T>> action, T arg)
        {
            return Action(action, arg, null);
        }

        public string Action<T>(Func<TController, Action<T>> action, T arg, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[] { arg }, routeValues);
        }

        #endregion

        #region 2 arguments

        public string Action<T1, T2>(Func<TController, Func<T1, T2, object>> action, T1 arg1, T2 arg2)
        {
            return Action(action, arg1, arg2, null);
        }

        public string Action<T1, T2>(Func<TController, Func<T1, T2, object>> action, T1 arg1, T2 arg2, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[] { arg1, arg2 }, routeValues);
        }

        public string Action<T1, T2>(Func<TController, Action<T1, T2>> action, T1 arg1, T2 arg2)
        {
            return Action(action, arg1, arg2, null);
        }

        public string Action<T1, T2>(Func<TController, Action<T1, T2>> action, T1 arg1, T2 arg2, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[] { arg1, arg2 }, routeValues);
        }

        #endregion

        #region 3 arguments

        public string Action<T1, T2, T3>(Func<TController, Func<T1, T2, T3, object>> action, T1 arg1, T2 arg2, T3 arg3)
        {
            return Action(action, arg1, arg2, arg3, null);
        }

        public string Action<T1, T2, T3>(Func<TController, Func<T1, T2, T3, object>> action, T1 arg1, T2 arg2, T3 arg3, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[] { arg1, arg2, arg3 }, routeValues);
        }

        public string Action<T1, T2, T3>(Func<TController, Action<T1, T2, T3>> action, T1 arg1, T2 arg2, T3 arg3)
        {
            return Action(action, arg1, arg2, arg3, null);
        }

        public string Action<T1, T2, T3>(Func<TController, Action<T1, T2, T3>> action, T1 arg1, T2 arg2, T3 arg3, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[] { arg1, arg2, arg3 }, routeValues);
        }

        #endregion

        #region 4 arguments

        public string Action<T1, T2, T3, T4>(Func<TController, Func<T1, T2, T3, T4, object>> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
        {
            return Action(action, arg1, arg2, arg3, arg4, null);
        }

        public string Action<T1, T2, T3, T4>(Func<TController, Func<T1, T2, T3, T4, object>> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[] { arg1, arg2, arg3, arg4 }, routeValues);
        }

        public string Action<T1, T2, T3, T4>(Func<TController, Action<T1, T2, T3, T4>> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
        {
            return Action(action, arg1, arg2, arg3, arg4, null);
        }

        public string Action<T1, T2, T3, T4>(Func<TController, Action<T1, T2, T3, T4>> action, T1 arg1, T2 arg2, T3 arg3, T4 arg4, object routeValues)
        {
            var c = DummyControllerCache<TController>.Instance;
            return Action(action(c).Method, new object[] { arg1, arg2, arg3, arg4 }, routeValues);
        }

        #endregion
    }
}
